Java同步锁synchronized用法的最全总结
1. 什么是同步锁?
在Java多线程编程中,同步锁是一种用于控制多线程并发访问的手段。它可以确保同一时间只有一个线程可以执行一段代码,从而保证线程安全。
synchronized关键字就是Java中最常用的同步锁。通过在方法或代码块上添加synchronized关键字,可以将这些方法或代码块变为同步代码,从而保证同一时间只有一个线程可以执行。
2. synchronized关键字的用法
2.1 修饰方法
在方法前加上synchronized关键字,可以将这个方法变成同步方法。当一个线程进入该方法时,会自动获取该方法所在对象的锁,其他线程只能等待锁被释放后才能进入该方法。
示例代码:
public synchronized void syncMethod(){
//需要同步的代码块
}
2.2 修饰代码块
synchronized关键字还可以用于修饰代码块,形式为synchronized(obj){},其中obj可以是对象的实例变量或者class对象。
当一个线程进入synchronized代码块时,会自动获取锁,其他线程只能等待锁被释放后才能进入。
示例代码:
public void syncBlock(){
synchronized(this){
//需要同步的代码块
}
}
2.3 synchronized关键字的工作原理
Java中的每个对象都有一个锁,当一个线程要访问一个加了synchronized关键字的方法或者代码块时,它必须先获得该对象的锁。如果该锁已经被其他线程获得了,那么该线程就会等待,直到该锁被释放。
3. synchronized关键字的注意事项
3.1 范围过大会影响性能
使用synchronized关键字时,需要注意将同步代码块的范围尽可能缩小,以减少锁竞争,提高程序性能。
3.2 不能跨线程传递锁
一个线程上了锁之后,其他线程是不能使用该锁的。所以不能将一个线程获得的锁传递给其他线程使用。
4. synchronized的替代方法
synchronized关键字的性能相对较低,还可能出现死锁等问题。可以考虑使用Lock接口及其实现类来代替synchronized关键字。
Lock接口提供了更加灵活和高效的锁控制机制,不同的实现类有不同的特点,可以根据需要进行选择。
5. 示例说明
5.1 示例1:银行账户取钱案例
这是一个简单的多线程取钱案例,其中涉及到银行账户余额的增减操作。为了保证线程安全,需要使用同步锁。
public class Account {
private double balance;// 账户余额
// 存款操作
public synchronized void deposit(double money) {
balance += money;
}
// 取款操作
public synchronized void withdraw(double money) {
if(balance>=money) {
balance -= money;
System.out.println(Thread.currentThread().getName()+" 取钱 "+money+" 元,余额为:"+balance);
}
else {
System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");
}
}
}
public class Test {
public static void main(String[] args) {
final Account account = new Account();//创建账户
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 3; j++) {//每个线程取3次钱
account.withdraw(100);//每次取100元
}
}
}, "Thread-" + i).start();//启动线程
}
}
}
5.2 示例2:多线程下载文件案例
多线程下载文件时,需要对文件下载操作进行同步,以保证多个线程不会同时写入同一个文件。
public class DownloadTask implements Runnable {
private String url;//文件地址
private String fileName;//文件名
public DownloadTask(String url, String fileName) {
this.url = url;
this.fileName = fileName;
}
public void run() {
try {
URLConnection conn = new URL(url).openConnection();
int contentLength = conn.getContentLength();//获取文件总长度
InputStream is = conn.getInputStream();//获取文件输入流
RandomAccessFile file = new RandomAccessFile(fileName, "rw");//使用RandomAccessFile进行文件写入
file.setLength(contentLength);//设置写入文件长度
file.close();
int threadNum = 5;//线程数
int blockSize = (contentLength + threadNum - 1) / threadNum;//每个线程需要下载的文件块大小
for (int i = 0; i < threadNum; i++) {
int start = i * blockSize;//文件块的起始位置
int end = (i == threadNum - 1) ? contentLength : start + blockSize - 1;//文件块的结束位置
new Thread(new BlockDownloadTask(i, start, end, url, fileName)).start();//启动线程,进行文件块下载
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 文件下载任务
class BlockDownloadTask implements Runnable {
private int blockId;//文件块编号
private int start;//文件块的起始位置
private int end;//文件块的结束位置
private String url;//文件地址
private String fileName;//文件名
public BlockDownloadTask(int blockId, int start, int end, String url, String fileName) {
this.blockId = blockId;
this.start = start;
this.end = end;
this.url = url;
this.fileName = fileName;
}
public void run() {
try {
File file = new File(fileName);//写入文件
RandomAccessFile raf = new RandomAccessFile(file, "rw");//使用RandomAccessFile进行文件写入
raf.seek(start);//指定写入位置
URLConnection conn = new URL(url).openConnection();
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);//设置下载的文件块区间
InputStream is = conn.getInputStream();//获取文件输入流
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {//读取输入流的数据,并写入文件
raf.write(buffer, 0, len);
}
raf.close();//关闭文件
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
String url = "http://www.example.com/bigfile.zip";//文件地址
String fileName = "bigfile.zip";//文件名
new Thread(new DownloadTask(url, fileName)).start();//启动下载任务
}
}
以上就是Java同步锁synchronized用法的最全总结和两个示例说明的详细攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java同步锁synchronized用法的最全总结 - Python技术站