package com.mytest.volatileandsynchronized;
public class MyService {
private boolean flag=true;//方法一:前面加volatile
//运行死循环方法
public void runMethod(){
while(flag){
//方法二:synchronized("aaa"){} 间接保证可见性
}
System.out.println("RunMethod end");
}
//改变循环条件
public void stopMethod(){
flag=false;
}
}
package com.mytest.volatileandsynchronized;
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService){
super();
this.myService=myService;
}
@Override
public void run() {
myService.runMethod();
}
}
package com.mytest.volatileandsynchronized;
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService){
super();
this.myService=myService;
}
@Override
public void run() {
myService.stopMethod();
}
}
package com.mytest.volatileandsynchronized;
public class MyRun {
public static void main(String[] args) throws Exception {
MyService service=new MyService();
ThreadA a=new ThreadA(service);
ThreadB b=new ThreadB(service);
a.start();
Thread.sleep(1000);
b.start();
System.out.println("已经发出停止指令了");
}
}
没有方法1和方法2,则无法跳出死循环:运行效果
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口
一种是继续Thread类:
package test;
public class ThreadOne extends Thread{
private String name;
public ThreadOne(String name){
this.name=name;
}
public void run(){
for(int i=0;i<5;i++){
System.out.println(name+" 运行 "+i);
}
try {
sleep((int)Math.random()*10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
ThreadOne thread01=new ThreadOne("A");
ThreadOne thread02=new ThreadOne("B");
thread01.start();
thread02.start();
}
}
package test;
public class ThreadTwo implements Runnable {
private String name;
public ThreadTwo(String name){
this.name=name;
}
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(name+" 运行 "+i);
}
try {
Thread.sleep((int)Math.random()*10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
ThreadTwo thread03=new ThreadTwo("C");
ThreadTwo thread04=new ThreadTwo("D");
new Thread(thread03).start();
new Thread(thread04).start();
}
}
同步是不错,但它并不完美。它有一些功能性的限制 —— 它无法中断一个正在等候获得锁的线程,也无法通过投票得到锁,如果不想等下去,也就没法得到锁。同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。
ReentrantLock 类
java.util.concurrent.lock
中的 Lock
框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为Lock
的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。
ReentrantLock
类实现了Lock
,它拥有与 synchronized
相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了synchronized
的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized
块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized
块时,才释放锁。
Lock
和 synchronized 区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。