多线程学习
线程的创建
使用Thread创建线程
直接新建一个线程,并调用start()方法
private void thread(){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread with thread start!");
}
});
thread.start();
}
// 或者使用给线程传入一个Runable对象
private void runnable() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Thread with runnable start!");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
使用ThreadFactory工厂类创建线程
使用AtomicInteger保证线程名称不重复,
private void threadFactory() {
AtomicInteger count = new AtomicInteger(0);
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"Thread-" + count.incrementAndGet());
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start!");
}
};
Thread thread = factory.newThread(runnable);
thread.start();
}
使用线程池创建或复用线程
当需要大量线程时,从性能内存的角度考虑应使用线程池,来避免大量临时对象的创建。
private void execute() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Thread with runnable thread pool start!");
}
};
// 创建一个有缓存的线程池
Executor executor = Executors.newCachedThreadPool();
// 创建只有一个线程的线程池
// Executor executor = Executors.newSingleThreadExecutor();
// 创建指定个数的线程池,适合短时间批量处理
// Executor executor = Executors.newFixedThreadPool(5);
// 创建一个可以延时处理或周期处理的线程池
// ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
executor.execute(runnable);
executor.execute(runnable);
executor.execute(runnable);
}
线程同步和线程安全
synchronized
synchronized的本质:保证方法内部或代码块内部资源的互斥访问,即同一时间,最多只有一个线程在访问资源,保证线程之间对监视资源的数据同步,即,任何线程在获取到Monitor后第一时间将资源拷贝到自己的缓存中,在释放Monitor的第一时间将缓存中的资源数据拷贝到共享内存中,从而确保其他线程在获取到Monitor时,拷贝的都是最新的资源数据;
synchronized 实现死锁
死锁的四个必要条件:
- 互斥条件:在一段时间内某资源只能由一个进程(线程)持有,此时若有其他进程(线程)请求该资源,则只能等待当前进程(线程)释放资源。
- 不可剥夺条件:进程(线程)获得资源后,在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程(线程)自己释放;
- 请求与保持条件:进程(线程)已经保持了一个资源,此时又请求其他资源,其他资源又被其他进程(线程)持有,导致进程(线程)被阻塞,并且对已保持资源不释放;
- 循环等待条件:两个进程(线程)同时持有对方需要的资源,导致进程被阻塞,无法释放资源。
private Object monitor1 = new Object();
private Object monitor2 = new Object();
private void creatDeadLock() {
new Thread(new Runnable() {
@Override
public void run() {
deadLock(monitor1, monitor2);
}
}, "Thread 1").start();
new Thread(new Runnable() {
@Override
public void run() {
deadLock(monitor2, monitor1);
}
}, "Thread 2").start();
}
private void deadLock(Object object1, Object object2) {
synchronized (object1) {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + "持有:" + object1.hashCode() + ", 并请求: " + object2.hashCode());
synchronized (object2) {
System.out.println("get object2 resource!");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 打印结果
Thread 2持有:2089229631, 并请求: 2009125064
Thread 1持有:2009125064, 并请求: 2089229631
// Thread 1 和 Thread 2请求相互持有的资源,同时又保持着对方的资源不释放,导致程序死锁
volatile
保证加了volatile关键子的字段的操作具有原子性和同步性,原子性相当于实现了针对单一字段的线程间互斥访问。
volatile只对基本数据类型(byte short int long float boolean double char)的赋值操作和对象的引用赋值操作有效;
线程安全问题本质
在多个线程访问共同的资源时,在某个线程对资源进程写操作时(写操作一开始,还未结束),其他线程对这个写了一半的资源进行读取,或者对这个正在写的资源也进行了写操作,导致出现错误数据。
锁机制的本质
通过对共享资源进行访问限制,让同一时间只有一个线程可以访问资源,保证数据的准确性。

浙公网安备 33010602011771号