package com.test;
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("新的线程开始运行");
}
public static void main(String[] args) {
System.out.println("main的主线程");
new MyThread().start();
//start方法调用以后,那么此时CPU有可能先执行新的线程。也有可能继续执行原来的线程(执行后续的代码)
System.out.println("main的主线程");
}
}
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("新的线程开始运行");
}
public static void main(String[] args) {
System.out.println("main的主线程");
Thread t= new MyThread();
System.out.println(t.getState());
t.start();
System.out.println(t.getState());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getState());
t.start(); //只有线程状态是NEW才可以调用start方法。
}
}
引起线程之间执行顺序的是竞争条件。
package com.spring;
public class MyThread extends Thread{
public int count=0;
@Override
public void run() {
count++;
}
public void countAdd(){
this.count++;
}
public static void main(String[] args) {
System.out.println("main的主线程");
MyThread t= new MyThread();
t.setPriority(MAX_PRIORITY);
t.start();
t.countAdd();
System.out.println(t.count);
System.out.println(t.count);
System.out.println(t.count);
System.out.println(t.count);
System.out.println(t.count);
}
}
并非只有静态变量才会有线程不安全,如上,如果count++不是原子操作的话,成员变量依然会不安全。因为资源是共享的,而run方法却在不同的线程中运行。
线程在真正执行的时候完全不是按照一条语句一条语句的执行。
比如:
if(count>0)
只要资源不共享,断续着的切换执行根本没有问题。只有资源共享才会有线程安全的问题
线程资源同步和线程之间的同步。
线程之间的同步就是比如A执行完之后要执行B,然后执行C。就是在线程之间有顺序的执行。
等待与阻塞:
等待是等待另外一个线程出现结果,或者另一个线程的调度,阻塞是没有获得锁
守护线程:
与守护进程是有区别的。当没有工作线程的时候,就停止了。
wait方法等待并释放这个锁。
对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。
有时候你会看到有所谓的类锁和对象锁的说法。
`假设我有一个类ClassA,其中有一个方法synchronized methodA(),那么当这个方法被调用的时候你获得就是对象锁,但是要注意,如果这个类有两个实例,比如:
ClassA a = new ClassA();
ClassA b = new ClassA();
那么如果你在a这对象上调用了methodA,不会影响b这个对象,也就是说对于b这个对象,他也可以调用methodA,因为这是两对象,所以说对象锁是针对对象的。`
在这里主要讨论一个使用上的问题,当我们使用sychronized锁住某个对象时,我们锁住的是这个引用本身,还是内存(堆)中的这个对象本身。对这个问题的一个延伸是,当我们在sychronized作用区域内,为这个引用附一个新值的时候,sychronized是否还有效?
先给出结论,sychronized锁住的是内存(堆)中的对象,当引用被附上新值的时候,则相当于旧对象的锁被释放。这里不做理论讨论,只是用程序进行验证。http://www.cnblogs.com/shipengzhi/articles/2223100.html
static synchronized 锁住的是整个类。会影响到所有的实例。
执行器
执行器是Java实现的线程池。
package com.ex;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Server {
private ThreadPoolExecutor executor;
public ThreadPoolExecutor getExecutor(){
return this.executor;
}
public Server(){
// executor=(ThreadPoolExecutor) Executors.newCachedThreadPool();// cache线程池在真正有任务的时候才初始化,随着任务变化而变化
executor=(ThreadPoolExecutor) Executors.newFixedThreadPool(10);//固定任务的线程池
System.out.println("总共线程池------------------------"+executor.getPoolSize());
System.out.println("活动的线程池数量---------------------"+executor.getActiveCount());
}
public void excuteTask(Task task){
executor.execute(task);
System.out.println("一共得线程池"+executor.getPoolSize());
System.out.println("活动的线程池数量,即正在处理任务的线程数量"+executor.getActiveCount());
}
public static void main(String[] args) {
Server server=new Server();
for(int i=0;i<100;i++){
Task task=new Task("线程id"+i);
server.excuteTask(task);
}
//主线程不断询问线程组是否执行完毕
while(true){
if(server.getExecutor().getCompletedTaskCount()==100){
System.out.println("总共线程池------------------------"+server.getExecutor().getPoolSize());
System.out.println("活动的线程池数量---------------------"+server.getExecutor().getActiveCount());
server.getExecutor().shutdown();
break;
}
}
}
}
package com.ex;
import java.util.Date;
public class Task implements Runnable{
private String name;
private Date date;
public Task(String name){
this.date=new Date();
this.name=name;
}
@Override
public void run() {
try {
System.out.println(this.name+"----开始执行任务");
Thread.sleep((long) (Math.random()*1000));
System.out.println(this.name+"----结束执行任务");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程的内存模型
对于公共变量或者资源,线程会复制一份到属于自己的线程栈,操作以后再放回公共资。在复制的过程中间,如果有其他线程修改了资源,那么复制的就不是最新的。这就是所谓的内存可见性问题。同步了当然不会存在这样的问题,因为同一个时刻,另外一个线程必读等待另一个线程读写完毕。
/**
*===============================================================
* @CopyRight: 北京逸生活技术服务有限公司技术部
* @author: xujianxing
* @date: 2016年3月16日
* @version: 1.0.0
*===============================================================
* 修订日期 修订人 描述
*/
package syncdemo;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* <p>TODO
* <p>TODO
* @author xujinaxing
* @date 2016年3月16日
* @see
* @since
* @modified TODO
*/
public class ThreadUnSafe extends Thread{
public static int flag=0;
public static List<Integer> list=new ArrayList<Integer>();
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
flag++;
//添加的时候必须同步。也就是说保证list是内存可见的。
synchronized (list) {
list.add(flag);
}
System.out.println(flag);
}
public static void main(String[] args) {
for(int i=0;i<10000;i++){
new ThreadUnSafe().start();
}
try {
Thread.currentThread().sleep(15000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int size=new HashSet<Integer>(ThreadUnSafe.list).size();
System.out.println("size"+size);
}
}
打印的值是9991.说明了线程之间由于存取不及时。导致set的size小于1000.
/**
*===============================================================
* @CopyRight: 北京逸生活技术服务有限公司技术部
* @author: xujianxing
* @date: 2016年3月16日
* @version: 1.0.0
*===============================================================
* 修订日期 修订人 描述
*/
package syncdemo;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>TODO
* <p>TODO
* @author xujinaxing
* @date 2016年3月16日
* @see
* @since
* @modified TODO
*/
public class ThreadSafe extends Thread{
public static AtomicInteger flag=new AtomicInteger(0);
public static List<Integer> list=new ArrayList<Integer>();
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i=flag.incrementAndGet();
synchronized (list) {
list.add(i);
}
System.out.println(flag.get());
}
public static void main(String[] args) {
for(int i=0;i<20000;i++){
new ThreadSafe().start();
}
try {
Thread.currentThread().sleep(15000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int size=new HashSet<Integer>(ThreadSafe.list).size();
System.out.println("size"+size);
}
}
上面的这个代码是线程安全的。性能高,是李利用硬件提供的原子操作。