Java 并发 --线程基本概念
线程的基本概念
线程是条单独的执行流,有自己的栈,和程序技术器
一般创建线程的方法有:
- 继承Thread
- 实现Runnable
- 实现Callable
线程的基本属性和方法
1. id和name
id是一个自增的id,每新建一个线程就+1
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
name 是Thread后面跟一串数字
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
2. 线程的优先级
线程的优先级是 1-10 默认是5
这个优先级的概念最终会被映射成操作系统的优先级,当然并不是10个优先级都能映射
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
3. 线程的状态
这个也是面试中,人家问的比较多的
新建状态: new状态指没有调用start()方法的线程
运行状态:线程启动之后状态start()调用之后,并且执行了run方法,并且没有阻塞
阻塞状态
等待 线程等待的是
时间等待 线程等待的是一个时间条件
结束 :线程运行结束后的状态
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
4.守护线程
一个主线程的辅助线程。
当主线程退出时,守护线程也就没意义了
5. Sleep方法
sleep方法会让线程睡眠指定的时间,单位是毫秒。
public static native void sleep(long millis) throws InterruptedException;
线程的睡眠过程中,会让出CPU,并且可以被中断。
6.yield方法
yield方法也是让出CPU,调用该方法,是告诉操作系统不着急占用CPU,可以让其他线程执行。
public class TestYield {
/**
* •Yield是一个静态的原生(native)方法
•Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
•Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
•它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
* @param args
*/
public static void main(String[] args) {
//yield是将当前运行的机会给相同优先级的线程。
Thread producer = new Producer();
Thread consumer = new Consumer();
// producer.setPriority(Thread.MIN_PRIORITY);
// consumer.setPriority(Thread.MAX_PRIORITY);
producer.setPriority(Thread.MIN_PRIORITY);
consumer.setPriority(Thread.MAX_PRIORITY);
producer.start();
consumer.start();
}
}
7. join方法
等待某个线程执行完之后,再继续执行
这个等待也可以被中断,如果被中断,会抛出异常
public final void join() throws InterruptedException {
join(0);
}
public class TestJoin {
/**
* 线程实例的方法join()方法可以使得一个线程在另一个线程结束后再执行
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(){
public void run(){
System.out.println("First Task Start ...");
System.out.println("First Task Sleep ....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("First Task Completed");
}
};
Thread t1 = new Thread(){
public void run(){
System.out.println("Second Task Start ...");
System.out.println("Second Task Completed");
}
};
t.start();
t.join();
t1.start();
}
}
8. 线程共享内存
线程之间是可以并行的,多个线程执行相同的一段代码,执行顺序不同,会导致最终出现意想不到的结果
public class CounterThread extends Thread {
private static int counter = 0;
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter++;
}
}
public static void main(String[] args) throws InterruptedException {
int num = 1000;
Thread[] threads = new Thread[num];
for (int i = 0; i < num; i++) {
threads[i] = new CounterThread();
threads[i].start();
}
// for (int i = 0; i < num; i++) {
// threads[i].join();
// }
System.out.println(counter);
}
}
上面的代码,每个线程执行一次counter++ 你想得到是1000*1000 ,实际上是得不到的
9.竞态条件
竞态条件是指,每个线程都有自己的栈和局部变量,当多个线程操作相同的变量时,会出现意想不到的结果,可能是正确的,也可能不是正确的。
上面的count++并不是原子性操作
有三个步骤
1. 去取内存中counter中的值
2. counter中的值+1
3. 将计算之后的值重新赋值给counter
但是这样就有个问题 ,线程1和线程2同时去取值 得到counter的值都是100 ,两个都分别+1之后,将值重新赋值给counter发现counter值还是101 ,这样和你预期的102就不一样了。
如何解决这个问题 有几种方式:
- 使用synchronized
- 使用显示锁Lock
- 使用原子变量 Atomic*
10.可见性,
其实也是内存可见性问题 , 对应一个线程 ,执行完操作后,先将数据写入CPU缓存中,最后更新到内存中。
在多线程的环境下,会有问题,一个线程对内存进行了修改,但是另外一个线程从内存中看不到就会出现问题。
为了解决可见性的问题,可以采用如下方式:
- 使用volatile变量
- 使用synchronized关键字或者显示锁进行同步。
public class TestVolatile {
private static int n = 10;
private static CountDownLatch runningThreadNum = new CountDownLatch(n);
/**
*
* 我们已经提到过可见性、有序性及原子性问题,通常情况下我们可以通过Synchronized关键字来解决这些个问题,
* 不过如果对Synchronized原理有了解的话,应该知道Synchronized是一个比较重量级的操作,对系统的性能有比较大的影响,
* 所以,如果有其他解决方案
* ,我们通常都避免使用Synchronized来解决问题。而volatile关键字就是Java中提供的另一种解决可见性和有序性问题的方案
* 。对于原子性,需要强调一点,也是大家容易误解的一点:对volatile变量的单次读/写操作可以保证原子性的,
* 如long和double类型变量,但是并不能保证i++这种操作的原子性,因为本质上i++是读、写两次操作。
*/
public volatile int inc = 0;
public void increase() {
inc++;
}
public static void main(String[] args) throws InterruptedException {
final TestVolatile test = new TestVolatile();
for (int i = 0; i < n; i++) {
new Thread() {
public void run() {
// for(int i = 0; i <1000;i++){
// //100次累加 ,最后结果不会是10000
// test.increase();
// }
test.increase();
runningThreadNum.countDown();
};
}.start();
}
while (Thread.activeCount() > 1)
// 保证前面的线程都执行完 , 不太准,如果线程run执行的时间比较长,就不准了 yield只是告诉操作系统不着急占用CPU,但是没说一定不占用
Thread.yield();
runningThreadNum.await();
System.out.println(test.inc);
}
}