java中使用多线程操作同一数据时,需要考虑到线程间的通讯,否则会出现数据错误,程序死锁卡死的情况。所以多线程在操作同一数据时必须考虑其先后顺序以保证数据准确,且不会出现死锁的情况。多线程间的通信往往是通过等待机制来实现的,等待某一线程执行完毕或者放弃内存后,再启动另一线程。常使用的两种方法:synchronized和线程锁Lock。
该方法通过多个线程通过同一个Runnable来实现,在执行该Runnable被synchronized修饰的方法时,需要其他线程也需要执行被synchronized修饰的方法时进入等待状态,放弃内存,等到其他线程执行完毕后,再执行。
(1)对代码块进行加锁
public class ThreadTest {
public static void main(String args[]){
MRunnable mRunnable = new MRunnable();
Thread thread1 = new Thread(mRunnable,"Thread1");
Thread thread2 = new Thread(mRunnable,"Thread2");
thread1.start();
thread2.start();
}
public static class MRunnable implements Runnable {
static int count = 0;//计数
@Override
public void run() {
synchronized(this){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+count);
count++;
}
}
}
}
}
该方法是通过多个线程持有同一个对象的引用,在执行该对象被synchronized修饰的方法时,需要其他线程也需要执行被synchronized修饰的方法时进入等待状态,放弃内存,等到其他线程执行完毕后,再执行。
(1)对代码块进行加锁
public class ThreadTest {
static Account account;
public static void main(String args[]){
account = new Account();
Thread1 thread1 = new Thread1(account);
Thread2 thread2 = new Thread2(account);
thread1.start();
thread2.start();
}
public static class Account{
int count = 0;//计数
}
public static class Thread1 extends Thread{
Account object;
public Thread1(Account object){
this.object = object;
}
@Override
public void run() {
super.run();
synchronized(object){
for(int i=0;i<5;i++){
System.out.println("Thread1:"+object.count);
object.count++;
}
}
}
}
public static class Thread2 extends Thread{
Account object;
public Thread2(Account object){
this.object = object;
}
@Override
public void run() {
super.run();
synchronized(object){
for(int i=0;i<5;i++){
System.out.println("Thread2:"+object.count);
object.count++;
}
}
}
}
}
(2)对方法进行加锁
public class ThreadTest {
static Account account;
public static void main(String args[]){
account = new Account();
Thread1 thread1 = new Thread1(account);
Thread2 thread2 = new Thread2(account);
thread1.start();
thread2.start();
}
public static class Account{
public synchronized void print1(){
for(int i=0;i<5;i++){
System.out.println("方法1");
}
}
public synchronized void print2(){
for(int i=0;i<5;i++){
System.out.println("方法2");
}
}
}
public static class Thread1 extends Thread{
Account object;
public Thread1(Account object){
this.object = object;
}
@Override
public void run() {
super.run();
object.print1();
}
}
public static class Thread2 extends Thread{
Account object;
public Thread2(Account object){
this.object = object;
}
@Override
public void run() {
super.run();
object.print2();
}
}
}
输出结果(等待某一线程执行完毕后剩下的线程才开始执行)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest {
static Lock lock;
static int count = 0;
public static void main(String args[]){
lock = new ReentrantLock();
Thread1 thread1 = new Thread1(lock);
Thread2 thread2 = new Thread2(lock);
thread1.start();
thread2.start();
}
public static class Thread1 extends Thread{
Lock lock;
public Thread1(Lock lock){
this.lock = lock;
}
@Override
public void run() {
super.run();
synchronized (lock){
for(int i=0;i<5;i++){
System.out.println("Thread1:"+count);
count++;
}
}
}
}
public static class Thread2 extends Thread{
Lock lock;
public Thread2(Lock lock){
this.lock = lock;
}
@Override
public void run() {
super.run();
synchronized (lock){
for(int i=0;i<5;i++){
System.out.println("Thread2:"+count);
count++;
}
}
}
}
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest {
static Lock lock;//当然这里我们还可以使用 ReentrantLock重入锁
static int count = 0;
public static void main(String args[]){
lock = new ReentrantLock();
Thread1 thread1 = new Thread1(lock);
Thread2 thread2 = new Thread2(lock);
thread1.start();
thread2.start();
}
public static class Thread1 extends Thread{
Lock lock;
public Thread1(Lock lock){
this.lock = lock;
}
@Override
public void run() {
lock.lock();
for(int i=0;i<5;i++){
System.out.println("Thread1:"+count);
count++;
}
lock.unlock();
}
}
public static class Thread2 extends Thread{
Lock lock;
public Thread2(Lock lock){
this.lock = lock;
}
@Override
public void run() {
lock.lock();
for(int i=0;i<5;i++){
System.out.println("Thread2:"+count);
count++;
}
lock.unlock();
}
}
}
volatile能保证指令的可见性,也能禁止重排序,所以在多个线程使用到这个关键字修饰的变量时,能够在它发生改变时,其他线程都能接收到它的改变,这样也保证了线程的安全。
volatile的使用条件:(1)对该变量的写操作不会依赖于当前值(不能是自增或自减操作)(2)该变量没有包含在具有其他变量的不变式中
new Thread(new Runnable() {
volatile boolean isRunning;//通过volatile修饰后可以避免出现因为boolean变量出现死锁
@Override
public void run() {
while (isRunning){
System.out.println("is Running");
}
}
}).start();
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){//通过是否被中断的标志来循环线程
System.out.println("is Running");
}
}
});
mThread.start();
mThread.interrupt();
不建议使用轮寻的方式来实现线程间的通讯,因为轮寻的方式线程并没有真正意义上的不执行,所以其所占用内存不会被释放,所以该方式不可取。