一 同步
同步方法,同步块。
synchronized methodA(), synchronized static methodA()
synchronized(this){ } , synchronized (XXX.class){ }
当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
一个线程进入一个对象的synchronized方法后,即获得了这个对象的锁,也就是占有了这个对象。这个对象的其他synchroized方法和方法块,是不能被其他线程所访问的。但这个对象的非synchroized方法,是可以被其它线程所访问的。这个方法同步代码块前有代码,还是可以访问的。
synchronized (XXX.class)和synchronized(this)前者锁的是该类的类对象的对象锁,后者锁的是当前对象。
package common;
public class SyTest {
private String a = "abc";
private String b = "def";
private void test1() throws InterruptedException {
// synchronized (this) {
synchronized (a.getClass()) { //synchronized (String.class) {
System.out.println("A");
Thread.sleep(5000);
}
}
private void test2() throws InterruptedException {
System.out.println("before synchronized B");
// synchronized (this) {
synchronized (b.getClass()) { //synchronized (String.class) {
System.out.println("B");
Thread.sleep(5000);
}
}
public static void main(String args[]) {
//final SyTest syTest = new SyTest();
new Thread() {
public void run() {
try {
// syTest.test1();
new SyTest().test1();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread() {
public void run() {
try {
// syTest.test2();
new SyTest().test2();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}.start();
}
}
打印出: 先打印前2行,过了5秒打印出B
A
before synchronized B
B
在没有对象的时候,使用类锁(该类的类对象的对象锁),如单例:
public class Singleton{
private Singleton(){}
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
二 线程池
1、ThreadPoolExecutor原理: http://thrillerzw.iteye.com/blog/1852760
多线程测试代码:
int numOfThreads =Runtime.getRuntime().availableProcessors()*2;
ExecutorService executor = Executors.newFixedThreadPool(numOfThreads);
List<Future<Long>> results = new ArrayList<Future<Long>>();
for (int i = 0; i < 500; i++) {
/*
*
<T> Future<T> submit(Callable<T> task)
* Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。
但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
call return Long (new Callable<Long> 中的)
*/
//添加的过程中,有的线程会开始执行了。
results.add(executor.submit(new Callable<Long>() {
@Override
public Long call() throws Exception {
long begin = System.currentTimeMillis();
try {
//测试对象,如向数据库插入数据
//test.insert();
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
return end - begin;
}
}));
}
// void shutdown()平滑关闭,停止接受任何新的任务且等待已经提交的任务执行完成(已经在执行的和还没有开始执行的)
// List<Runnable> shutdownNow()强制关闭, 试图停止当前正执行的task,并返回尚未执行的task的list
executor.shutdown();
//线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。
while(!executor.awaitTermination(1, TimeUnit.MINUTES)){
System.out.println("线程池没有关闭");
}
System.out.println("线程池已经关闭");
long sum = 0;
for (Future<Long> result : results) {
sum += result.get();
}
System.out.println("running time: " + sum + "ms");
三、其它
什么代码是始终为线程安全的、是不需要同步的。如下:
1)常量始终是线程安全的,因为只存在读操作。
2)对构造器的访问(new 操作)是线程安全的,因为每次都新建一个实例,不会访问共享的资源。
3)最重要的是:局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量。