JAVA多线程学习笔记

前阵子,重新复习并学习了有关多线程的知识,下面是我的一些学习笔记。每一点,只为尽量简明的表达出知识内容,不会全部具体展开讲,并附上相关代码。(其实,我现在也还做不到全部展开来讲啦,啊哈哈),日后这个笔记会不断补充更新。

1.能中断的线程


public class Test1 {
    public static void main(String[] args) throws Exception {
        Thread thread = new Thread() {
            public void run() {
                while (!this.isInterrupted()) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        System.out.println("如果在睡眠(sleep)状态或者等待(wait)下被中断的话,就会进入到这里");
                        return;
                    }
                    System.out.println(1111);
                }
            };
        };
        thread.start();
        Thread.sleep(100);
        thread.interrupt();
    }
}

tips:1.上面实际上,展示了两种中断线程的方法,一种是通过while循环,不断的判断this.isInterrupted()状态。不过这种判断方法,有一个缺点,就是万一循环里面的操作十分耗时,如果我们想在那操作完成前,就中断线程的话,其实是不可以的。2.利用异常的方法,中断线程也是可以的,但是缺点跟上面一样,我们不能依靠这种方法,中断一个正在进行严重耗时操作的线程,因为要么已经跳过了sleep或者还没到达sleep(wait应用场景就不一样了,这里不展开讨论),所以是进不了catch的。

所以,我们可以得到一个结论:interrupt()并不直接中断线程,而是设定一个中断标识,然后由程序进行中断检查,确定是否中断。


2.线程可以利用suspend方法(暂停),和resume方法(恢复),对线程进行暂停和恢复。需要注意的是,suspend()方法是不释放锁的,也就是说如果某个线程在同步代码块中暂停了,那其他线程,就无法再进入,利用这个同步(监听)对象的同步代码块里面了。而且这两个方法都是过时被废弃的方法。


3.synchronized锁重入

在使用synchronized时,当一个线程得到一个对象锁之后,再次请求此对象锁时,是可以再一次得到这个对象的锁的。(本来就得到了嘛= =)这也证明了在一个synchronized方法或者synchronized代码块内部,调用依赖于这个对象锁的synchronized方法/块时,是永远可以得到锁的。可重入锁也支持在父子类继承的环境中。即:

public synchronized void aa() {
		// 某些操作
		// 当一个方法进行到这里之后,因为bb方法也需要获得锁,而此时当前线程已经获得了这个当前对象的对象锁,所以bb方法的锁是可以马上获得的。即不会被其他的线程先获得锁,然后先执行了bb方法这样。
		bb();
		// 某些操作
	}

	public synchronized void bb() {
		// 某些操作
	}

4. Future.cancel()更安全,条理,高效的中断线程


public boolean cancel(boolean mayInterruptIfRunning)
从接口 Future 复制的描述
试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用  cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则  mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。

此方法返回后,对 Future.isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 Future.isCancelled() 的后续调用将始终返回 true

指定者:
接口  Future<V> 中的  cancel
参数:
mayInterruptIfRunning - 如果应该中断执行此任务的线程,则为  true;否则允许正在运行的任务运行完成
返回:
如果无法取消任务,则返回  false,这通常是由于它已经正常完成;否则返回  true


5.当一个线程执行的代码出现异常时,其所持有的锁会自动释放


6.同步不具有继承性,即父类的某个方法是有synchronized关键字的,但是子类继承父类时覆盖了这个方法后,把synchronized关键字去掉了的话,这个子类的这个方法就不再是同步的方法了。


7.synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上,是给对象加锁。而Class锁可以对类的所有对象实例起作用。


8.关键字volatile的作用,就是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。但是,它只能保证可见性,并不能保持原子性,就是它是非线程安全的。


9.关键字synchronized可以使多个线程访问同一个资源具有与同步性,而且它还具有将线程工作内存中的私有变量与公共内存中变量同步的功能。

public class Test2 {

	public static void main(String[] args) {
		final A a = new A();

		new Thread() {
			public void run() {
				a.startMethod();
			};
		}.start();

		try {
			// 模拟一些耗时操作等,确保上面的线程已经执行了startMethod
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		new Thread() {
			public void run() {
				a.stopMethod();
			};
		}.start();
	}
}

class A {
	// private volatile boolean isStop = false;如果使用volatile关键字的话,就不需要同步代码块了
	private boolean isStop = false;

	public void startMethod() {
		while (!isStop) {
			// 如果变量不用volatile修饰,这里又不加这个同步代码块的话,当A线程调用这个类的一个实例的startMethod后,B线程调用再调用这个实例的stopMethod方法的话,while循环还是不会停止的
			// synchronized (this) {
			//
			// }
		}
		System.out.println("停下来了");
	}

	public void stopMethod() {
		System.out.println("结束了吗?");
		isStop = true;
	}
}


10.在调用wait之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait方法。在执行wait方法之后,当前线程释放锁


11.在执行notify方法之后,当前线程不会马上释放该对象锁,要等到退出synchronized代码块范围之后,当前线程才会释放锁。当已经wait的方法,重新获得锁执行下面的代码时,也是如此。也是要执行到退出synchronized代码块范围之后再释放锁。


12.ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。即即使将它弄成static(static ThreadLocal<Integer> local = new ThreadLocal<Integer>();),并且让每一个线程都往里面添加数据,最终取出来的数据,都是自己线程的数据,不会被其他线程所干扰:并且还可以复写里面的initialValue方法,这样第一次get的时候,就不会得到空值。


13.创建一个Timer就是启动一个新的线程,这个新启动的线程并不是一个守护线程(守护线程的意思就是,如果当前没有非守护线程,只剩下守护线程时,守护线程会自动结束),它会一直在运行。new Timer(true);这样可以创建一个守护线程的Timer 。



14.TimerTask类中的cancel方法的作用是将自身从任务队列中清除。Timer类中的cancel方法的作用是将任务队列中的全部任务清空。但是,需要注意的是Timer类中的cancel方法有时不一定会停止执行计划任务,而是正常执行。这是因为Timer类中cancel方法有时并没有争抢到queue锁,所以TimerTask类中的任务继续正常执行。


15.ThreadGroup线程组,既可以添加线程也可以添加线程组,方便统一管理里面的线程。


16.ReentrantLock:一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

private ReentrantLock lock = new ReentrantLock();
    public void aa(String s) {
        lock.lock();
        try {
            System.out.println("进来了");
            Thread.sleep(1000);
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


17.使用Condition实现等待/通知(代替notify和wait)

public class Test {
    public static void main(String args[]) throws Exception {
        new Thread() {
            @Override
            public void run() {
                await("aaa");
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                signal("bbb");
            }
        }.start();
    }

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();

    public static void await(String s) {
        lock.lock();
        try {
            System.out.println("准备等待");
            condition.await();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void signal(String s) {
        lock.lock();
        try {
            System.out.println("准备唤醒");
            condition.signal();
            System.out.println(s);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}



tip:
输出结果为: 准备等待
准备唤醒
bbb
aaa
很明显,也是跟notify一样,马上是执行了唤醒,等待的线程也不能马上获得锁,必须等到退出同步代码块之后,这里就是必须等到调用 lock.unlock();方法之后,等待的线程才能重新获得锁并重新继续运行下去。而且,很明显的是,如果你只想单独唤醒某些线程时,你只需要不断的使用 lock.newCondition(),获取更多的Condition即可。

18.ReentrantLock 里面的tryLock()方法的作用是:仅在调用时锁定未被另一个线程保持的情况下,才获取该锁。即其中一个线程获得锁后,会执行if为true里面的方法,没有获得锁的线程,会执行false里面的方法,并且不会一直阻塞等待获取锁.
  if (lock.tryLock()) {
            System.out.println(s + "拿到锁了");
        } else {
            System.out.println(s + "没有拿到锁");
        }


19.ReentrantLock类具有完全排他的效果,效率比较低。ReentrantReadWriteLock读写锁效率更高。它一共包含两种锁,一个是读操作的相关锁,称为共享锁readWriteLock.readLock().lock();另一个是写操作相关的锁,称为排它锁readWriteLock.writeLock().lock();需要注意的是,只有是共享锁时,多个线程才能同时进入。如果遇到的是,排它锁时,跟之前的效果是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值