package Threadinfo;
public class MyThread implements Runnable{
private boolean flag = true;
private int num = 0;
@Override
public void run() {
while(flag)
{
System.out.println(Thread.currentThread().getName()+"-->"+num++);
}
}
public void stop()
{
this.flag = !this.flag;
}
}
package Threadinfo;
/**
* Thread.currendThread() 当前线程
* setName() 设置名称
* getName() 获取名称
* isAlive() 判断状态
*/
@SuppressWarnings("all")
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
MyThread it = new MyThread();
//Thread(Runnable target, String name) 如果不取名字,将是一个自动编号的名称。
Thread proxy = new Thread(it,"挨踢");//(自定义线程,名字)
proxy.setName("test");
System.out.println(proxy.getName());//test
//main线程
System.out.println(Thread.currentThread().getName());
proxy.start();
System.out.println("启动后的状态:"+proxy.isAlive());//true
proxy.sleep(5);
it.stop();
proxy.sleep(5);
System.out.println("停止后的状态:"+proxy.isAlive());//false
}
}
package Threadinfo;
/**
* 优先级:代表概率,不是绝对的优先级,不代表绝对的先后顺序
* MAX_PRIORITY 10
* NORM_PRIORITY 5(默认)
* MIN_PRIORITY 1
* setPriority()设置优先级
* getPriority()
*/
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
MyThread it1 = new MyThread();
Thread p1 = new Thread(it1,"挨踢1");//(自定义线程,名字)
MyThread it2 = new MyThread();
Thread p2 = new Thread(it2,"挨踢2");//(自定义线程,名字)
//没有设置优先级就是普通(默认)优先级
p1.setPriority(Thread.MIN_PRIORITY);//设置优先级
p2.setPriority(Thread.MAX_PRIORITY);
p1.start();
p2.start();
Thread.sleep(100);
it1.stop();
it2.stop();
}
}
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。由于我们可以通过private
关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized
关键字,它包括两种用法:synchronized
方法和synchronized
块,同步块锁定的是引用类型
,this
,类.class
。
同步:并发 多个线程访问同一份资源确保资源安全。–>线程安全
Test1 //线程不安全
Test2 //线程安全 锁定正确
test3 //线程安全 锁定正确
test4 //线程安全 锁定范围(过小)不正确 可能出现问题
test5 //线程不安全 锁定资源不正确
test6 //线程安全 锁定不正确
package Threadsyn;
public class Demo01 {
public static void main(String[] args) {
//真实角色
Web12306 web = new Web12306();
//代理
Thread t1 = new Thread(web,"111");
Thread t2 = new Thread(web,"222");
Thread t3 = new Thread(web,"333");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
class Web12306 implements Runnable{
private int num = 20;
private boolean flag = true;
@Override
public void run() {
while(flag)
{
test5();
}
}
//线程不安全
public void test1()
{
if(num<=0)
{
flag = false;//跳出循环
return;
}
try {
Thread.sleep(500);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
//线程安全(效率降低了) 只能被一个线程进行访问
public synchronized void test2()
{
if(num<=0)
{
flag = false;//跳出循环
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
//线程安全 锁定正确
public void test3()
{
//a b c
synchronized (this) {
if(num<=0)
{
flag = false;//跳出循环
return;
}
try {
Thread.sleep(500);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
//线程安全 锁定范围(过小)不正确 可能出现问题
public void test4()
{
//a b c
synchronized (this) {
if(num<=0)
{
flag = false;//跳出循环
return;
}
}
try {
Thread.sleep(500);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
//线程不安全 锁定资源不正确
public void test5()
{
//a b c
synchronized ((Integer)num) {
if(num<=0)
{
flag = false;//跳出循环
return;
}
try {
Thread.sleep(500);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
//线程安全 锁定不正确
public void test6()
{
if(num<=0)
{
flag = false;//跳出循环
return;
}
//a b c
synchronized (this) {
try {
Thread.sleep(500);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
}
我们常用的单例设计模式的懒汉式
就在进行多线程时,就使用了线程同步。
package Threadsyn;
/**
* 单例设计模式:确保一个类只有一个对象
*/
public class Demo02 {
public static void main(String[] args) {
Jvm jvm1 = Jvm.getInstance();
Jvm jvm2 = Jvm.getInstance();
//单线程jvm1与jvm2地址一样,如果是多线程可能就不一样了,存在延时。
System.out.println(jvm1);
System.out.println(jvm2);
System.out.println("------------");
JvmThread thread1 = new JvmThread(2000);
JvmThread thread2 = new JvmThread(2000);
thread1.start();
thread2.start();
}
}
class JvmThread extends Thread{
private long time;
public JvmThread() {
}
public JvmThread(long time) {
this.time = time;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+"-->创建:"+Jvm.getInstance1(time));
}
}
/**
* 单例设计模式
* 确保一个类只有一个对象
* 懒汉式 双重检查
* 1、构造器私有化,避免外部直接创建对象
* 2、声明一个私有的静态变量
* 3、创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象。
*/
class Jvm
{
//声明一个私有的静态变量
private static Jvm instance = null;
//构造器私有化,避免外部直接创建对象
private Jvm(){
}
//创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
public static Jvm getInstance()
{
if(null==instance){
instance = new Jvm();
}
return instance;
}
//用多线程是访问的地址可能不一样 没有同步不需要等待,可能发生错误。
public static Jvm getInstance1(long time)
{
if(null==instance){
try {
Thread.sleep(time);//延时,放大错误发生的概率
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
return instance;
}
//创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
//同步方法,这样就可以避免错误。
public static synchronized Jvm getInstance2(long time)
{
if(null==instance)
{
try {
Thread.sleep(time);//延时,放大错误发生的概率
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
return instance;
}
//创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
public static Jvm getInstance3(long time)
{
//a b -->效率不高 c 存在对象也需要等待(同getInstance2方法效率差不多)
synchronized(Jvm.class)
{
if(null==instance)
{
try {
Thread.sleep(time);//延时,放大错误发生的概率
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
}
return instance;
}
//创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
public static Jvm getInstance4(long time)
{
//c d e -->提高效率 提供已经存在对象的访问效率
if(null==instance)
{
//a b
synchronized (Jvm.class) { //第一个创建了,之后以后就可以直接使用。
if(null==instance){
try {
Thread.sleep(time);//延时,放大错误发生的概率
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
}
}
return instance;
}
}
package Threadsyn;
/**
* 单例设计模式
* 确保一个类只有一个对象 //外部只能使用对象,不能创建对象。
* 懒汉式
* 1、构造器私有化
* 2、声明私有的静态属性
* 3、对外提供访问属性的静态方法,确保该对象存在
*/
public class MyJvm {
private static MyJvm instance = null;
private MyJvm(){
}
public static MyJvm getInstance()
{
if(null==instance)//提高效率
{
synchronized (MyJvm.class) {
if(null==instance)//安全
{
instance = new MyJvm();
}
}
}
return instance;
}
}
/**
* 饿汉式
* 1、构造私有化
* 2、声明私有的静态属性,同时创建对象
* 3、对外提供访问属性的静态方法,确保该对象存在
*/
class MyJvm2 {
private static MyJvm2 instance = new MyJvm2();//类加载时创建
private MyJvm2(){
}
public static MyJvm2 getInstance(){
return instance;
}
}
/**
* 使用了内部类,类在使用的时候才加载 延缓了加载时间 提高效率
*/
class MyJvm3 {
private MyJvm3(){
}
public static MyJvm3 getInstance(){
return JVMholder.instance;
}
private static class JVMholder{
private static MyJvm3 instance = new MyJvm3();
}
}
本博主更加详细的单例设计模式
http://blog.csdn.net/scgaliguodong123_/article/details/42550599
package Threadsyn;
/**
* 模拟死锁
* 过多的同步方法可能造成死锁
* 一个把商品的资源获取了,一个把钱的资源获取了
*/
public class Demo03 {
public static void main(String[] args)
{
//同一份资源,不同的线程
Object g = new Object();
Object m = new Object();
//一手给钱
Test1 t1 = new Test1(g,m);
//多态不能使用新增方法
//Runnable proxy = new Thread(t1);
Thread proxy = new Thread(t1);
proxy.start();
//一手给货
Test2 t2 = new Test2(g,m);
Thread proxy2 = new Thread(t2);
proxy2.start();
}
}
class Test1 implements Runnable
{
Object goods ;
Object money ;
public Test1(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while(true)
{
test();
}
}
public void test()
{
synchronized (goods) {
try {
Thread.sleep(500);//延时,加大发生错误的可能性。
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(money){
}
}
System.out.println("一手给货");
}
}
class Test2 implements Runnable
{
Object goods ;
Object money ;
public Test2(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while(true)
{
test();
}
}
public void test()
{
synchronized (money) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(goods)
{
}
}
System.out.println("一手给钱");
}
}
package Threadprocon;
/**
* 一个场景,共同的资源
* 生产者消费者模式 信号灯法
* wait(): 等待 释放锁
* sleep:休眠 不释放锁
* notify()/notifyAll():唤醒
* notify、wait要与synchronized一起使用才起作用
*/
public class Movie {
private String pic;
//信号灯
//flag-->T 生产者生产,消费等待,生产完成后,通知消费
//flag-->F 消费者消费,生产者等待,消费完后,通知生产
private boolean flag = true;
/**
* 播放
* @param pic
*/
public synchronized void play(String pic)
{
if(!flag)//生产者等待
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始生产
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//生产完毕
this.pic = pic;
System.out.println("生产电影:"+pic);
//通知消费
this.notify();
//生产者停下
this.flag = false;
}
public synchronized void watch()
{
if(flag)//消费者等待
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始消费
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("看电影:"+pic);
//消费完毕
//通知生产
this.notify();
//消费停止
this.flag = true;
}
}
package Threadprocon;
/**
* 生产者
* @author liguodong
*/
public class Player implements Runnable{
private Movie m;
public Player(Movie m) {
super();
this.m = m;
}
@Override
public void run() {
for(int i=0;i<20;i++){
if(0==i%2){
m.play("何以笙箫默");
}else{
m.play("功夫之王");
}
}
}
}
package Threadprocon;
/**
* 消费者
* @author liguodong
*/
public class Watcher implements Runnable{
private Movie m;
public Watcher(Movie m) {
super();
this.m = m;
}
@Override
public void run() {
for(int i=0;i<20;i++){
m.watch();
}
}
}
package Threadprocon;
public class Demo01 {
public static void main(String[] args) {
//共同的资源
Movie m = new Movie();
//多线程
Player p= new Player(m);
Watcher w = new Watcher(m);
new Thread(p).start();
new Thread(w).start();
}
}
运行结果:
生产电影:何以笙箫默
看电影:何以笙箫默
生产电影:功夫之王
看电影:功夫之王
1、Timer
使用 Timer 实现任务调度的核心类是 Timer 和 TimerTask。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。
java.util.Timer
定时器类—- Timer本身就是一个线程,只是这个线程是用来调用其他线程的
java.util.TimerTask
任务类 —-而TimerTask类是一个抽象类,该类实现了Runnable接口,该类具备多线程的能力。
在这种实中现方式中,Timer类实现的是类似闹钟的功能,也就是定时或者每隔一定时间触发一次线程。然后通过继承TimerTask使该类获得多线程的能力,将需要多线程执行的代码书写在run方法内部,然后通过Timer类启动线程的执行。
在实际使用时,一个Timer可以启动任意多个TimerTask实现的线程,但是多个线程之间会存在阻塞。如果多个线程之间需要完全独立运行的话,最好还是一个Timer启动一个TimerTask实现。
Timer 的设计核心是一个 TaskQueue 和一个 TaskThread。Timer 将接收到的任务丢到自己的 TaskQueue 中,用于定时器线程共享,定时生产任务,通过各种调度,定时执行任务,当他们过时的时候将他们从队列中删除。TimerThread 在创建 Timer 时会启动成为一个守护线程。这个线程会轮询所有任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread 被唤醒并执行该任务。之后 TimerThread 更新最近一个要执行的任务,继续休眠。
Timer的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务。
package Threadschedule;
/**
* 了解任务调度
* Timer()
*
* 运行一次
* schedule(TimerTask task, Date time)
*
* 起始时间开始执行,以后每个多少秒执行一次。
* schedule(TimerTask task, Date firstTime, long period)
*/
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Demo01 {
public static void main(String[] args) {
Timer timer = new Timer();//计数器
//schedule(TimerTask task, Date firstTime, long period)
timer.schedule(new TimerTask(){
int iNum=1;
@Override
public void run() {
System.out.println("女神来啦,屌丝"+(iNum++)+"号就位。");
}
},
new Date(System.currentTimeMillis()+1000),
1000);
}
}
运行结果:
女神来啦,屌丝1号就位。
女神来啦,屌丝2号就位。
女神来啦,屌丝3号就位。
女神来啦,屌丝4号就位。
2、ScheduledExecutor
由于 Timer存在的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor
。
其设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时ScheduledExecutor 都是在轮询任务的状态。
package Threadschedule;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo02 implements Runnable {
private String jobName = "";
public Demo02(String jobName) {
super();
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("execute " + jobName);
}
public static void main(String[] args) {
//创建一个线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
long initialDelay1 = 1;
long period1 = 1;
// 从现在开始1秒钟之后,每隔1秒钟执行一次job1 时间固定
service.scheduleAtFixedRate(
new Demo02("job1"), initialDelay1,
period1, TimeUnit.SECONDS);//TimeUnit.SECONDS表示以秒为单位
long initialDelay2 = 2;
long delay2 = 2;
// 从现在开始2秒钟之后,每隔2秒钟执行一次job2 时间不固定
service.scheduleWithFixedDelay(
new Demo02("job2"), initialDelay2,
delay2, TimeUnit.SECONDS);
}
}
运行结果:
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
以上用到了ScheduledExecutorService 中两种最常用的调度方法ScheduleAtFixedRate
和 ScheduleWithFixedDelay
。
ScheduleAtFixedRate 每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为 :initialDelay, initialDelay+period, initialDelay+2*period, …
;
ScheduleWithFixedDelay 每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay
。
由此可见,ScheduleAtFixedRate 是基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度。
当然除了以上两种任务调度之外,还有其他的任务调度方式,这里并没有阐述,如开源工具包 Quartz、开源工具包 JCronTab。
对于简单的基于起始时间点与时间间隔的任务调度,使用 Timer 就足够了;
如果需要同时调度多个任务,基于线程池的 ScheduledTimer 是更为合适的选择;
当任务调度的策略复杂到难以凭借起始时间点与时间间隔来描述时,Quartz 与 JCronTab 则体现出它们的优势。
熟悉 Unix/Linux 的开发人员更倾向于 JCronTab,且 JCronTab 更适合与 Web 应用服务器相结合。
Quartz 的 Trigger 与 Job 松耦合设计使其更适用于 Job 与 Trigger 的多对多应用场景。
原文地址:http://blog.csdn.net/scgaliguodong123_/article/details/46117279