java等待5秒_实战java高并发程序设计第二章(连更)

 1.线程的基本状态  2.线程的基本操作  3. volatile与java内存模型  4.线程组  5.守护线程(Daemon)  6.线程优先级  7.线程安全与synchronized  8.隐蔽错误 
19a0fbd2a5f40f43b0391b26214367ab.gif

1.线程的基本状态

  1. 线程的生命周期
7b4cb980b79236c28f58ab3410b92531.png

2.线程的基本操作

①.新建线程

 Thread tl=new Thread(){ @override public void run(){ System.out.println("Hel1o,I am t1"); }; t1.start(); t1.start();

②.终止线程

Thread.stop() //立即终止线程所有活动

stop()方法在结束线程时,会直接终止线程,并立即释放这个线程所持有的锁,可能引起数据不一致,强烈建议不使用!!

 //正确停止线程的方法 public static class ChangeObjectThread extends Thread { volatile boolean stopme = false; //标记位,记录是否要停止线程  public void stopMe(){ stopme = true; } @Override public void run() { while (true) { if (stopme){ //在合适的地方停止线程,避免数据不一致 System.out.println("exit by stop me"); break; } synchronized (u) { int v = (int) (System.currentTimeMillis() / 1000); u.setId(v); //Oh, do sth. else try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } u.setName(String.valueOf(v)); } Thread.yield(); } } }

③.线程中断

public void Thread.interrupt() //中断线程,仅发送通知,设置标记位,等待合适时机线程中断public boolean Thread.isInterrupted() //判断是否被中断,判断标记位public static boolean Thread.interrupted()//判断是否被中断,并清除当前中断状态
Thread tl=new Thread(){ @Override public void run(){ while(true){ if(Thread.currentThread().isInterrupted()){ //需要设立中断处理方法,否则无法响应中断 System.out.println("Interruted!"); break; ) Thread.yield();}
public class InterruputSleepThread { public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(){ @Override public void run(){ while(true){ if(Thread.currentThread().isInterrupted()){ System.out.println("Interruted!"); break; } try { Thread.sleep(2000); //由于中断而抛出异常会清除中断标记 } catch (InterruptedException e) { System.out.println("Interruted When Sleep"); //sleep被中断后,重置标记位继续运行,保证数据一致性和完整性 //设置中断状态 Thread.currentThread().interrupt(); } Thread.yield(); } } }; t1.start(); Thread.sleep(2000); t1.interrupt(); }}

④.等待(wait)和通知(notify)

public final void wait() throws InterruptedException;public final native void notify();

object.wait()和object.notify()方法必须包含在synchronized关键字中,因为要获取对象的监视器(monitor对象)

public class SimpleWN { final static Object object = new Object(); public static class T1 extends Thread{ public void run() { synchronized (object) { System.out.println(System.currentTimeMillis()+":T1 start! "); try { System.out.println(System.currentTimeMillis()+":T1 wait for object "); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T1 end!"); } } } public static class T2 extends Thread{ public void run() { synchronized (object) { System.out.println(System.currentTimeMillis()+":T2 start! notify one thread"); object.notify(); System.out.println(System.currentTimeMillis()+":T2 end!"); try { Thread.sleep(2000); //此处sleep()2秒,T1此时仍然需要等待2秒才会继续执行 } catch (InterruptedException e) { } } } } public static void main(String[] args) { Thread t1 = new T1() ; Thread t2 = new T2() ; t1.start(); t2.start(); //输出: //1565249714094:T1 start!  //1565249714094:T1 wait for object  //1565249714094:T2 start! notify one thread //1565249714094:T2 end! //1565249716094:T1 end! }} 
 wait()和sleep()的区别:1.wait必须在同步条件内使用,sleep无此要求2.wait释放锁,sleep不会释放3.wait为Object内的实例方法,sleep为Thread的静态方法4.wait需要调用对象的notify方法唤醒,sleep知道时间结束或者被interrupt打断

⑤.挂起(suspend)和继续执行(resume)

废弃方法,不作介绍!!有需求可使用wait和notify

⑥.等待线程结束(join)和谦让yield

public final void join() throws InterruptedException //一直阻塞线程直至结束public final synchronized vold join(long millis)throws InterruptedException //给出最大等待时间

join的机制实际为在方法调用线程循环wait,如下join()核心代码:

while(isAlive()){ wait(0);}
public static native void yield();

yield()方法表示当前线程让出cpu计算资源,但还是会参与cpu资源的争夺,一般用于防止低优先级线程占用太多cpu资源

3. volatile与java内存模型

volatile关键字可以保证一定的原子性,但是不能替代锁,不能保证100%线程安全volatile可以保证可见性和有序性,充当内存栅栏,禁止指令重拍

4.线程组

 当一个系统中线程较多且功能分配比较明确是可以使用线程组 
public class ThreadGroupName implements Runnable { public static void main(String[] args) { ThreadGroup tg = new ThreadGroup("PrintGroup"); Thread t1 = new Thread(tg, new ThreadGroupName(), "T1"); Thread t2 = new Thread(tg, new ThreadGroupName(), "T2"); t1.start(); t2.start(); System.out.println(tg.activeCount()); //2 tg.list(); //java.lang.ThreadGroup[name=PrintGroup,maxpri=10] // Thread[T1,5,PrintGroup] // Thread[T2,5,PrintGroup] } @Override public void run() { String groupAndName = Thread.currentThread().getThreadGroup().getName() + "-" + Thread.currentThread().getName(); while (true) { System.out.println("I am " + groupAndName); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }}

activeCount()获取线程组中的活动线程数

list()则打印线程组中的所有线程信息

5.守护线程(Daemon)

守护线程在后台执行,如垃圾回收线程,与之对应的为用户线程,当守护线程要守护的对象不存在了,则守护线程自动退出

当一个java应用中只有守护线程时,java虚拟机自动退出

public class DaemonDemo { public static class DaemonT extends Thread{ public void run(){ while(true){ System.out.println("I am alive"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Thread t=new DaemonT(); t.setDaemon(true); //此方法必须在start方法前 t.start();  Thread.sleep(2000); }}

上述代码中 t被设置为守护线程,当主线程退出时,守护线程也会退出;若未设置为守护线程,主线程退出后,t线程将依旧打印.

6.线程优先级

线程可以设置优先级,优先级高的线程获取到cpu资源的概率会更大(但不是一定会比低优先级的线程先获取到cpu资源)
public class PriorityDemo { public static class HightPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ //此处锁住该类是为了让产生一次资源竞争,使优先级差异表现更为明显 count++; if(count>10000000){ System.out.println("HightPriority is complete"); break; } } } } } public static class LowPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ count++; if(count>10000000){ System.out.println("LowPriority is complete"); break; } } } } } /** * HightPriority先完成的次数多,但是 不保证 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread high=new HightPriority(); LowPriority low=new LowPriority(); high.setPriority(Thread.MAX_PRIORITY); //此方法可以设置优先级 0-10 数字 low.setPriority(Thread.MIN_PRIORITY); low.start(); high.start(); }}
试验多次HightPriority总是能比LowPriority快,但是不保证一定是这样.

7.线程安全与synchronized

synchronized为重量级同步锁,实现线程间的同步,sychronize锁住的对象、方法和代码块,每一次只能有一个线程进行访问,从而保证了线程的安全 用法: 指定锁对象: synchronized(object){.....}指定实例方法 : public synchronized void println()指定静态方法: public static synchronized void println()
public class BadAccountingSync2 implements Runnable{ static int i=0; public static synchronized void increase(){ //此处如果不适用static则为线程不安全,最终i值必定小于20000 i++; } @Override public void run() { for(int j=0;j<10000;j++){ increase(); } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(new BadAccountingSync2()); Thread t2=new Thread(new BadAccountingSync2()); t1.start();t2.start(); t1.join();t2.join(); System.out.println(i); //输出20000 }}

8.隐蔽错误

  • 数值溢出
int v1=1073741827;int v2=1431655768;System.out,println("vl="+v1); //1073741827System.out.println("v2="+v2); //1431655768int ave=(vl+v2)/2;System.out,println("ave="+ave); //-894784850 数值溢出,两int数值相加超过int最大值时溢出
  • 并发不安全的arraylist
public class ArrayListMultiThread { static ArrayList al = new ArrayList(10); public static class AddThread implements Runnable { @Override public void run() { for (int i = 0; i < 1000000; i++) { al.add(i); } } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(new AddThread()); Thread t2=new Thread(new AddThread()); t1.start(); t2.start(); t1.join();t2.join(); System.out.println(al.size()); }}
以上程序可能会有三个结果1.正常结束,打印值为2000000 (运气好)2.正常结束,打印值小于2000000 (一般情况下会出现这种并发不安全的现象)3.抛出异常,arrayindexoutofbound(扩容过程中内部一致性遭破坏,导致数组越界)如何避免此问题?使用线程安全的vector代替arraylist
  • 并发不安全的hashmap
public class JDK8HashMapMultiThread { static Map map = new HashMap(); static int nullNum = 0; //记录空值数 public static class AddThread implements Runnable { int start = 0; public AddThread(int start) { this.start = start; } @Override public void run() { for (int i = start; i < 10000; i += 2) { map.put(Integer.toString(i), Integer.toString(i)); } } } public static void test() throws Exception { Thread t1 = new Thread(new JDK8HashMapMultiThread.AddThread(0)); Thread t2 = new Thread(new JDK8HashMapMultiThread.AddThread(1)); t1.start(); t2.start(); t1.join(); t2.join(); } public static void main(String[] args) throws Exception { test(); for (int i = 0; i < 10000; i++) { if (map.get("" + i) == null) { nullNum += 1; } } System.out.println(10000 - nullNum); //hashmap中存有的entry数 System.out.println(map.size()); //hashmap中size属性的值 }}
打印值为: 6573 9715打印结果不一定,但可以看出线程一定不安全,原因在于:1.put时,两个entry的key hash值一致,同时进入插入代码,导致只有一个值插入成功2.插入成功后size数值 进行size++,非原子操作,导致并发不安全注意: 另网上有其他博文说会产生resize闭链问题,此问题只存在jdk1.8版本以前,1.8版本已优化过hashmap,故不存在闭链问题想要避免并发问题可以使用concurrenthashmap
  • 错误的加锁
public class BadLockOnInteger implements Runnable{ public static Integer i=0; //integer为不变类型,对其加锁无效 static BadLockOnInteger instance=new BadLockOnInteger(); @Override public void run() { for(int j=0;j<1000000;j++){ synchronized(i){// synchronized(instance){ //改为对instance加锁即线程安全了 i++; } } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start();t2.start(); t1.join();t2.join(); System.out.println(i); //打印值小于2000000  }}
注意:对于不变对象如基本类型 Integer String 等加锁,相当于每次都锁不同的对象,从而导致并发问题产生.

喜欢的话记得关注和转发哦~会一直更新的哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值