一.synchronized方法和synchronized代码块
synchronized方法 只有一个线程能访问,只有这个方法执行完之后其他线程才能访问
synchronized方法的锁是当前对象,也就是如果new多次是不能阻止多个线程访问代码的
public classSyncTest {
/**
*对于SyncTest的对象 只有一个线程能访问
*对于不同的SyncTest的对象可以有多个线程访问
*/
public synchronized void say1(){
System.out.println("hello get the lock1!");
//休眠200s不释放锁
try{
Thread.sleep(200*1000);
}catch(InterruptedException e) {
e.printStackTrace();}
}
/**
* 与synchronized方法相同
*/
public void say2(){
synchronized(this){
System.out.println("hello get the lock2!");
try{
Thread.sleep(200*1000);
}catch(InterruptedException e) {
e.printStackTrace();}
}
}
/**
* 同一时间只有一个线程能访问
* 与synchronized方法不同,这样即使多次new也跟使用同一个对象是一样的 只有一个线程能访问
**/
public void say3(){
synchronized(SyncTest.class){
System.out.println("hello get the lock3!");
try{
Thread.sleep(200*1000);
}catch(InterruptedException e) {
e.printStackTrace();}
}
}
}
测试运行同步方法和同步代码块:
1.测试say1()方法发现刚开始时只打印了一段,而说明只有一个线程访问了。
2.同步方法默认是锁当前类对象,在多次new的时候,并不能起到锁方法的作用。
3.同步代码段可以锁类,这样即使多次new也跟使用同一个对象是一样的,只有一个线程能访问。
举例子:
最为线程安全类 最常见的做法就是加synchronized方法
StringBuilder 线程不安全
StringBuffer 线程安全:看StringBuffer源码,可以看见 所有方法都加了synchronized
HashMap线程不安全
HashTable 线程安全
二.Thread类常用方法
名称 new Thread(String name) 和 new Thread(Runnable run,String name)
获取当前: Thread Thread.current();
获取当前所有Thread的数量:Thread.activeCount();
eg:
三.线程变量ThreadLocal
ThreadLocal是一个特殊的模板变量 每个线程获取到的都是自己的值,看源码就知道 其实是利用了Thread.currentThread(),然后利用了 map结构存储,所以ThreadLocal中的map map.get(Thread.currentThread()); 返回的就是不同的值。
public class ThreadLocalTest{
private static ThreadLocal<ThreadLocalTest> threadLocal=new ThreadLocal<>();;
private static ThreadLocaTest mainThreadLocal;
/**
* 创建当前线程的ThreadLocal对象
*/
public static void prepare(){
if(threadLocal.get()==null){
threadLocal.set(new ThreadLocalTest());
}
}
public static void prepareMain(){
if(mainThreadLocal!=null){
throw new RuntimeException("只有一个线程可以调用prepareMain");
}
mainThreadLocal=new ThreadLocalTest();
if(threadLocal.get()==null){
threadLocal.set(mainThreadLocal);
}
}
public static ThreadLocalTest getMain(){
return mainThreadLocal;
}
public static ThreadLocalTest myThreadTest(){
return threadLocal.get();
}
private ThreadLocalTest(){
}
}
public static voidshowThreadLocal(){
ThreadLocalTest.prepareMain();
new Thread(new Runnable() {
@Override
public void run() {
if(ThreadLocalTest.getMain()!=ThreadLocalTest.myThreadTest()){
console.info(Thread.currentThread().getName()+"当前不是主线程");
}else{
console.info(Thread.currentThread().getName()+"当前是主线程");
}
}
}).start();
if(ThreadLocalTest.getMain()!=ThreadLocalTest.myThreadTest()){
console.info(Thread.currentThread().getName()+"当前不是主线程");
}else{
console.info(Thread.currentThread().getName()+"当前是主线程");
}
}
其实现原理 利用的是Thread.currentThread();
四.concurrent包的CountDownLatch
一个辅助类,初始化的时候指定数据个数,没调用一次countDown()数字减少一个。
一个方法 countDown() 一个方法await()。
public static void CountDownLatchTests() throws InterruptedException {
long startTime=System.currentTimeMillis();
CountDownLatch countDownLatch=new CountDownLatch(65535);
ThreadPool threadPool=new ThreadPool(10000);
for(inti=0;i<65535;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
try{
countDownLatch.await();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程完成了!"+(System.currentTimeMillis()-startTime)+"ms");
}
eg:线程A , B ,C。 A,B运行完后运行C
CountDownLatch countDownLatch=new CountDownLatch(2);
new Thread(newRunnable() {
@Override
public void run() {
try{
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" done !");
countDownLatch.countDown();
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" done !");
countDownLatch.countDown();
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
try{
countDownLatch.await();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" done !");
}
},"C").start();
五.线程池初步
线程池基本思想就是 创建一些线程复用
long startTime=System.currentTimeMillis();
CountDownLatch countDownLatch=new CountDownLatch(65535);
ThreadPool threadPool=new ThreadPool(10000);
for(inti=0;i<65535;i++){
new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(200);
countDownLatch.countDown();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
try{
countDownLatch.await();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程完成了!"+(System.currentTimeMillis()-startTime)+"ms");
上面的代码预计消耗时间是200ms但是 实际远远超出了我们的预期,这是因为新建线程消耗了一部分时间,cpu拥挤,并不会有那么多线程同时运行,会排队一会。
通过线程池可以优化上述代码的速度:
public class IdleThread extends Thread {
public LinkedBlockingQueuequeue;
public boolean started = true;
public IdleThread(LinkedBlockingQueuequeue) {
this.queue = queue;
}
@Override
public void run() {
while (started) {
try {
//这个poll表示获取一个Runnable如果没有等待100ms
Runnable runnable = queue.poll(100, TimeUnit.MILLISECONDS);
if(runnable!=null)
runnable.run();
} catch (InterruptedException e) {
}
}
}
public void kill() {
started = false;
}
}
public class ThreadPool {
/**
*池最小大小
*/
private int minSize;
/**
*池最大大小
*/
private int maxSize;
LinkedBlockingQueue<Runnable> tasks;
List<IdleThread> idleThreads;
public ThreadPool(int size){
this.maxSize=size;
this.minSize=size;
init();
}
private void init(){
tasks=new LinkedBlockingQueue<>(minSize);
idleThreads=new ArrayList<>(minSize);
for(inti=0;i<minSize;;i++){
IdleThread thread=new IdleThread(tasks);
idleThreads.add(thread);
thread.start();
}
}
public void execute(Runnable runnable) throws InterruptedException {
tasks.put(runnable);
}
public void shutdown(){
try{
for(IdleThread idleThread :idleThreads) {
idleThread.kill();
idleThread.interrupt();
}
}catch(Exception e){
e.printStackTrace();;
}
tasks.clear();
}
public void waitForAll(){
while(tasks.size()>0){
try{
Thread.sleep(0,1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}