1、创建线程
java中创建线程有两种方法,1是继承Thread类并重载其run()方法,2是实现Runable接口,并实现其run()方法
从源码中可看出,其实Thread最终也是要实现Runable接口,所以在一般使用中,都会直接实现Runable接口。
注意:如果即实现了Runable接口有继承Thread类并重载了其run()方法,那么会直接执行被重载后的方法
2、Timer类
当new一个Timer对象时,一般调用其schedule方法用以调度任务,调用schedule方法传入的第一个参数为一个TimerTask的对象,所以new这个对象,并且重写其run()方法用以指出义务逻辑
3、synchronized关键字
synchronized关键字用以实现多线程同步机制,同步机制对资源进行加锁,使得在同一时间,只有一个线程可以进行操作。当synchronized方法执行完或发生异常时,会自动释放锁。锁是上在代表要操作的资源的类的内部方法中,而不线程代码中。是对同一个对象上锁。
synchronized关键字使用方法主要有同步块,或同步方法。如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不synchronized方法所在的对象,而是synchronized方法所在类所对应的class对象。java中,无论一个类有多少对象,但仅有一个class对象。
synchronized方法是一种粗粒度的并发控制。某一时刻,只能有一个线程执行该synchronized方法
synchronized块则是一种细粒度的并发控制,只会将块的代码同步,位于方法内。synchronized块之外的其他代码是可以被多个线程同时访问到的。
4、进程间通信
这里先所说的进程间通信即使让两个进程交替执行,所用到的就是wait()和notify()方法,常用的办法是设置一个boolean类型变量对于使线程等待,然后notify()方法唤醒线程。注意:这里的wait()和notify()都必须是对同一个对象进行操作
wait()和sleep()的区别:
sleep()让线程等待,但不释放资源,等待时间结束不一定立刻执行,而是取决于CPU的调度
wait()释放等待资源,可以去执行另外的任务,用notify()方法唤醒
5、线程范围内的共享变量(每个线程操作的各自线程内的值)
ThreadLocal:采用ThreadLocal的set方法和get方法可以实现共享。缺点:一个ThreadLocal变量只能放一个数据。可以新建类进行封装。
采用单例模式的思想
public static MyThreadScopeData getThreadInstance(){ MyThreadScopeData instance = map.get(); if(instance ==null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; } private static ThreadLocal<MyThreadScopeData> map =new ThreadLocal<MyThreadScopeData>(); private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }
6、多个线程共享同一个数据
1、如果每个线程执行方法相同,可以使用同一个Runnable对象,比如卖票系统。
2.1、如果每个线程执行代码不同,就需要不同的Runnable对象,共享数据封装到类中。
7、悲观锁和乐观锁
悲观锁:悲观锁假定冲突总会发生(一定会发生并发问题),读数据和写数据会想回阻塞。synchronized就是典型的悲观锁
乐观锁:乐观锁并非假定什么时候都发生并发问题,它执行操作,在遇到并发问题的时候回退。典型的乐观锁为concurrent包中的原子类(如AtomicInteger等)。